/*! ******************************************************************************
 *
 * Created on 2021年9月25日
 *
 * Copyright(c) YuZhou Big Data Laboratory CO.,Ltd, 2022.  All rights reserved.
 *
 *******************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************************/
package org.eclipse.swt.widgets;

import java.util.List;

import org.apache.commons.lang.ArrayUtils;
import org.eclipse.jface.window.DefaultToolTip;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.plugins.JobEntryPluginType;
import org.pentaho.di.core.plugins.Plugin;
import org.pentaho.di.core.plugins.PluginInterface;
import org.pentaho.di.core.plugins.StepPluginType;
import org.yzbdl.lanius.core.plugins.metastore.LdrCategoryEntity;
import org.yzbdl.lanius.core.plugins.metastore.LdrClassificationEntity;
import org.yzbdl.lanius.core.plugins.metastore.LogicType;
import org.yzbdl.lanius.core.plugins.metastore.LdrPluginEntity;
import org.pentaho.di.ui.core.ConstUI;
import org.pentaho.di.ui.core.gui.GUIResource;
import org.pentaho.di.ui.spoon.Spoon;
import org.pentaho.di.ui.spoon.TreeSelection;
import org.pentaho.di.ui.spoon.dialog.MetaStoreExplorerDialog;
import org.pentaho.di.ui.spoon.job.JobGraph;
import org.pentaho.di.ui.spoon.trans.TransGraph;
import org.pentaho.di.ui.util.StringUtils;
import org.pentaho.ui.xul.XulComponent;
import org.pentaho.ui.xul.components.XulMenuitem;
import org.pentaho.ui.xul.containers.XulMenu;
import org.pentaho.ui.xul.containers.XulMenupopup;
import org.pentaho.ui.xul.jface.tags.JfaceMenu;
import org.pentaho.ui.xul.jface.tags.JfaceMenuitem;
import org.pentaho.ui.xul.jface.tags.JfaceMenupopup;
import org.yzbdl.lanius.core.plugins.service.CategoryService;
import org.yzbdl.lanius.core.plugins.service.ClassificationService;

/**
 * @author tanpeng@yzbdl.ac.cn
 * @version 1.0
 * @date 2022-08-02 11:19
 */
public class LaniusClassTree extends Tree {
    private static Class<?> PKG = Spoon.class;

    private Spoon spoon;
    private LaniusClassExpandBar expandBar;
    private DefaultToolTip toolTip;

    private ClassificationService classificationService = ClassificationService.getInstance();
    private LdrClassificationEntity ldrClassificationEntity;

    private CategoryService categoryService = CategoryService.getInstance();

    public LaniusClassTree(LaniusClassExpandBar expandBar, int style, Spoon spoon, DefaultToolTip toolTip,
                           LdrClassificationEntity ldrClassificationEntity) {
        super(expandBar, style);
        this.spoon = spoon;
        this.expandBar = expandBar;
        this.toolTip = toolTip;
        this.ldrClassificationEntity = ldrClassificationEntity;

        this.addSelectionListener(new LaniusClassTreeSelectionListener());
        this.addTreeListener(new LaniusClassTreeListener());
        this.addMouseMoveListener(new LaniusClassTreeMouseMoveListener());
        this.addMouseListener(new LaniusClassTreeMouseListener());
        this.addMenuDetectListener(new LaniusClassTreeMenuDetectListener());
        this.addKeyListener(new LaniusClassTreeKeyListener());
    }

    /**
     * 监听选中事件
     */
    class LaniusClassTreeSelectionListener extends SelectionAdapter {
        @Override
        public void widgetSelected(SelectionEvent event) {
            if (spoon.props.getAutoCollapseCoreObjectsTree()) {
                TreeItem[] selection = LaniusClassTree.this.getSelection();
                if (selection.length == 1) {
                    TreeItem top = selection[0];

                    if (top instanceof LaniusCategoryTreeItem) {
                        boolean expanded = top.getExpanded();
                        for (TreeItem item : LaniusClassTree.this.getItems()) {
                            item.setExpanded(false);
                        }
                        top.setExpanded(!expanded);

                        LdrCategoryEntity ldrCategoryEntity = ((LaniusCategoryTreeItem)top).getCategoryEntity();
                        if (!expanded) {
                            categoryService.collapseCategory();
                            categoryService.expandCategory(ldrCategoryEntity.getId());
                        } else {
                            categoryService.collapseCategory(ldrCategoryEntity.getId());
                        }
                    }
                }
            }
        }
    }

    /**
     * 监听树的事件
     */
    class LaniusClassTreeListener extends TreeAdapter {
        @Override
        public void treeExpanded(TreeEvent treeEvent) {
            if (spoon.props.getAutoCollapseCoreObjectsTree()) {
                TreeItem treeItem = (TreeItem)treeEvent.item;
                for (TreeItem item : LaniusClassTree.this.getItems()) {
                    item.setExpanded(false);
                }
                treeItem.setExpanded(true);
                LaniusClassTree.this.setSelection(treeItem);
                if (treeItem instanceof LaniusCategoryTreeItem) {
                    LdrCategoryEntity ldrCategoryEntity = ((LaniusCategoryTreeItem)treeItem).getCategoryEntity();
                    categoryService.collapseCategory();
                    categoryService.expandCategory(ldrCategoryEntity.getId());
                }
                if (treeItem instanceof LaniusPluginVersionTreeItem) {
                    LaniusClassTree.this.setSelection(treeItem);
                }
            }
        }

        @Override
        public void treeCollapsed(TreeEvent e) {
            TreeItem treeItem = (TreeItem)e.item;

            if (treeItem instanceof LaniusCategoryTreeItem) {
                LdrCategoryEntity ldrCategoryEntity = ((LaniusCategoryTreeItem)treeItem).getCategoryEntity();
                categoryService.collapseCategory(ldrCategoryEntity.getId());
            }
        }
    }

    class LaniusClassTreeMouseMoveListener implements MouseMoveListener {

        @Override
        public void mouseMove(MouseEvent move) {
            if (!spoon.props.showToolTips()) {
                return;
            }
            int expandItemIndex = 0;
            for (int i = 0; i < expandBar.getItemCount(); i++) {
                if (expandBar.getItem(i).getExpanded()) {
                    expandItemIndex = i;
                }
            }
            toolTip.hide();
            TreeItem item = searchMouseOverTreeItem(LaniusClassTree.this.getItems(), move.x, move.y);
            if (item instanceof LaniusPluginTreeItem) {
                LdrPluginEntity ldrPluginEntity = ((LaniusPluginTreeItem)item).getPluginEntity();

                Image image = item.getImage();
                if (image == null) {
                    toolTip.hide();
                } else {
                    toolTip.setImage(image);

                    String tip = ldrPluginEntity.getProfile();
                    if (ldrPluginEntity.getUsageCount() > 0) {
                        tip = tip + "(" + ldrPluginEntity.getUsageCount() + ")";
                    }
                    tip = Const.NVL(tip, "");
                    toolTip.setText(ldrPluginEntity.getName() + Const.CR + Const.CR + tip);
                    toolTip.setBackgroundColor(GUIResource.getInstance().getColor(255, 254, 225));
                    toolTip.setForegroundColor(GUIResource.getInstance().getColor(0, 0, 0));
                    if (expandItemIndex < 1) {
                        Point point = new Point(item.getBounds().x + 10, item.getBounds().y + 60);
                        toolTip.show(new org.eclipse.swt.graphics.Point(point.x, point.y));
                    } else {
                        Point point = new Point(item.getBounds().x + 10, item.getBounds().y - 30 + 5 * expandItemIndex);
                        toolTip.show(new org.eclipse.swt.graphics.Point(point.x, point.y));
                    }
                }
            }
        }

        private TreeItem searchMouseOverTreeItem(TreeItem[] treeItems, int x, int y) {
            for (TreeItem treeItem : treeItems) {
                if (treeItem.getBounds().contains(x, y)) {
                    return treeItem;
                }
                if (treeItem.getItemCount() > 0) {
                    treeItem = searchMouseOverTreeItem(treeItem.getItems(), x, y);
                    if (treeItem != null) {
                        return treeItem;
                    }
                }
            }
            return null;
        }
    }

    class LaniusClassTreeMouseListener extends MouseAdapter {
        @Override
        public void mouseDoubleClick(MouseEvent event) {
            boolean shift = (event.stateMask & SWT.SHIFT) != 0;
            // 商店插件的双击需要单独处理
            if (LogicType.MARKET.getId() == ldrClassificationEntity.getLogicType()) {
                doubleClickedInMarketTree(event, LaniusClassTree.this, shift);
            } else {
                doubleClickedInTree(LaniusClassTree.this, shift);
            }
        }

        /**
         * 响应核心对象树鼠标双击时间
         *
         * @param tree  核心对象树
         * @param shift 是否上档
         * @author hs@yzbdl.ac.cn
         */
        private void doubleClickedInMarketTree(MouseEvent event, Tree tree, boolean shift) {}

        /**
         * 响应核心对象树鼠标双击时间
         *
         * @param tree  核心对象树
         * @param shift 是否上档
         * @author hs@yzbdl.ac.cn
         */
        private void doubleClickedInTree(Tree tree, boolean shift) {
            TreeSelection[] objects = spoon.delegates.tree.getDesignTreeSelection(tree, true);
            if (objects.length != 1) {
                return; // not yet supported, we can do this later when the OSX bug
                // goes away
            }

            TreeSelection object = objects[0];

            final Object selection = object.getSelection();
            // final Object parent = object.getParent();

            if (selection instanceof Class<?>) {
            } else {
                if (selection instanceof PluginInterface) {
                    PluginInterface plugin = (PluginInterface)selection;
                    if (plugin.getPluginType().equals(StepPluginType.class)) {
                        TransGraph transGraph = spoon.getActiveTransGraph();
                        if (transGraph != null) {
                            transGraph.addStepToChain(plugin, shift);
                        }
                    }
                    if (plugin.getPluginType().equals(JobEntryPluginType.class)) {
                        JobGraph jobGraph = spoon.getActiveJobGraph();
                        if (plugin.getId().trim().equals("SPECIAL")) {
                            MessageBox mb = new MessageBox(spoon.getShell(), SWT.ICON_WARNING);
                            mb.setMessage("请拖拽此插件至作业面板");
                            mb.setText("提示");
                            mb.open();
                            return;
                        }
                        if (jobGraph != null) {
                            jobGraph.addJobEntryToChain((Plugin)object.getSelection(), shift);
                        }
                    }
                }
                spoon.editSelectionTreeExtension(selection);
            }
        }

        @Override
        public void mouseDown(MouseEvent mouseEvent) {
            if (mouseEvent.button == SWT.MouseDown) {
                // Console.log("BUTTON2");
                TreeItem[] treeItems = LaniusClassTree.this.getSelection();
                if (treeItems.length != 0) {
                    return;
                }
                XulMenupopup pluginMenu = (XulMenupopup)spoon.menuMap.get("plugin-menu");
                List<XulComponent> xulComponents = pluginMenu.getChildNodes();
                for (XulComponent xulcomponent : xulComponents) {
                    xulcomponent.setVisible(false);
                }
                XulMenuitem viewDetail = (XulMenuitem)pluginMenu.getElementById("category-menu-add");
                viewDetail.setVisible(true);
                viewDetail.setCommand("spoon.categoryNew('" + null + "', '" + ldrClassificationEntity.getId() + "', '"
                    + ldrClassificationEntity.getPluginTypeClass() + "')");
                ConstUI.displayMenu(pluginMenu, LaniusClassTree.this);
            }
        }
    }

    class LaniusClassTreeMenuDetectListener implements MenuDetectListener {
        @Override
        public void menuDetected(MenuDetectEvent event) {
            TreeItem[] treeItems = LaniusClassTree.this.getSelection();
            if (treeItems.length != 1) {
                return;
            }
            TreeItem treeItem = treeItems[0];

            setPluginMenu(treeItem);
        }

        /**
         * 设置栏目上面的右键菜单
         */
        private synchronized void setPluginMenu(TreeItem treeItem) {
            if (treeItem instanceof LaniusPluginTreeItem) {
                LdrPluginEntity ldrPluginEntity = ((LaniusPluginTreeItem)treeItem).getPluginEntity();

                XulMenupopup pluginMenu = (XulMenupopup)spoon.menuMap.get("plugin-menu");

                // 移除所有子菜单
                List<XulComponent> xulComponents = pluginMenu.getChildNodes();
                for (XulComponent xulcomponent : xulComponents) {
                    xulcomponent.setVisible(false);
                }

                // 因对应页面未开发 暂时屏蔽
                // “查看插件详情”菜单 (插件具有versionId才能查看)
                if (false && StringUtils.isNotBlank(ldrPluginEntity.getVersion())) {
                    XulMenuitem viewDetail = (XulMenuitem)pluginMenu.getElementById("plugin-menu-viewdetail");
                    viewDetail.setVisible(true);
                    viewDetail.setCommand("spoon.viewPluginDetail('" + ldrPluginEntity.getVersion() + "', '"
                        + ldrPluginEntity.getName() + "')");
                }

                // “安装插件”菜单 (云端插件栏目才能下载)
                if (LogicType.MARKET.getId() == ldrClassificationEntity.getLogicType()) {
                    XulMenuitem install = (XulMenuitem)pluginMenu.getElementById("plugin-menu-install");
                    install.setVisible(true);
                    install.setCommand("spoon.installPlugin('" + ldrPluginEntity.getVersion() + "', '"
                        + ldrPluginEntity.getPluginId() + "', '" + ldrPluginEntity.getPluginTypeName() + "')");
                }

                // “安装插件至”菜单 (云端插件栏目才能下载)
                if (LogicType.MARKET.getId() == ldrClassificationEntity.getLogicType()) {
                    XulMenu installTo = (XulMenu)pluginMenu.getElementById("plugin-menu-installto");
                    installTo.setVisible(true);
                    XulMenupopup addto = (XulMenupopup)installTo.getElementById("plugin-menu-installto-child");
                    for (XulComponent c : addto.getChildNodes()) {
                        addto.removeChild(c);
                    }

                    // FuncType funcType = FuncType.getById(classificationEntity.getFuncType());

                    List<LdrClassificationEntity> classifications =
                        classificationService.getAll(ldrClassificationEntity.getPluginTypeClass(), LogicType.CUSTOMIZE);
                    for (int i = 0; i < classifications.size(); i++) {
                        LdrClassificationEntity ce = classifications.get(i);
                        if (ce.getId() == ldrClassificationEntity.getId()) {
                            continue;
                        }
                        XulMenuitem sdm = new JfaceMenuitem(null, addto, spoon.getMainSpoonContainer(), "sdm_" + i, i);
                        sdm.setLabel(ce.getName());
                        Integer classificationId = ce.getId();
                        // sdm.setCommand("spoon.installPluginTo('" + pluginEntity.getVersionId() + "', '" +
                        // classificationId + "')");
                        sdm.setCommand("spoon.installPluginTo('" + ldrPluginEntity.getVersion() + "', '"
                            + ldrPluginEntity.getPluginId() + "', '" + ldrPluginEntity.getPluginTypeName() + "', '"
                            + classificationId + "', '" + ldrPluginEntity.getId() + "')");
                        addto.addChild(sdm);
                    }
                }

                // “从分组中移除”菜单 (只有自定义分类 有"从分组中移除"按钮)
                if (LogicType.CUSTOMIZE.getId() == ldrClassificationEntity.getLogicType()) {
                    XulMenuitem remove = (XulMenuitem)pluginMenu.getElementById("plugin-menu-remove");
                    remove.setVisible(true);
                    LdrCategoryEntity ldrCategoryEntity = ((LaniusCategoryTreeItem)treeItem.getParentItem()).getCategoryEntity();
                    remove.setCommand(
                        "spoon.removePlugin('" + ldrPluginEntity.getId() + "', '" + ldrCategoryEntity.getId() + "')");

                }

                // 添加插件至菜单(非云端栏目才能操作)
                if (LogicType.MARKET.getId() != ldrClassificationEntity.getLogicType()) {
                    XulMenu addtoMenu = (XulMenu)pluginMenu.getElementById("plugin-menu-addto");
                    addtoMenu.setVisible(true);

                    XulMenupopup addto = (XulMenupopup)addtoMenu.getElementById("plugin-menu-addto-child");
                    for (XulComponent c : addto.getChildNodes()) {
                        addto.removeChild(c);
                    }
                    // FuncType funcType = FuncType.getById(classificationEntity.getFuncType());

                    List<LdrClassificationEntity> classifications =
                        classificationService.getAll(ldrClassificationEntity.getPluginTypeClass(), LogicType.CUSTOMIZE);
                    for (int i = 0; i < classifications.size(); i++) {
                        LdrClassificationEntity ce = classifications.get(i);
                        if (ce.getId() == ldrClassificationEntity.getId()) {
                            continue;
                        }

                        JfaceMenu sdm = new JfaceMenu(null, addto, spoon.getMainSpoonContainer(), "sdm_" + i);
                        sdm.setLabel(ce.getName());
                        addto.addChild(sdm);

                        XulMenupopup itemPop =
                            new JfaceMenupopup(null, sdm, spoon.getMainSpoonContainer(), "item_pop_" + i);
                        List<LdrCategoryEntity> categoryEntities =
                            CategoryService.getInstance().getAllByClassificationId(ce.getId());

                        sdm.addChild(itemPop);
                        if (ArrayUtils.isNotEmpty(categoryEntities.toArray())) {
                            for (int j = 0; j < categoryEntities.size(); j++) {
                                LdrCategoryEntity ge = categoryEntities.get(j);
                                JfaceMenuitem cem =
                                    new JfaceMenuitem(null, itemPop, spoon.getMainSpoonContainer(), "cem_" + j, j);
                                cem.setLabel(ge.getName());
                                cem.setId(ge.getId().toString());
                                Integer CategoryId = ge.getId();
                                cem.setCommand(
                                    "spoon.addPluginTo('" + ldrPluginEntity.getId() + "', '" + CategoryId + "')");
                                itemPop.addChild(cem);
                            }
                        }

                    }
                }

                ConstUI.displayMenu(pluginMenu, LaniusClassTree.this);
            }
            if (treeItem instanceof LaniusCategoryTreeItem) {
                Integer classificationEntityId = ldrClassificationEntity.getId();
                XulMenupopup pluginMenu = (XulMenupopup)spoon.menuMap.get("plugin-menu");
                LdrCategoryEntity ldrCategoryEntity = ((LaniusCategoryTreeItem)treeItem).getCategoryEntity();
                // 移除所有子菜单
                List<XulComponent> xulComponents = pluginMenu.getChildNodes();
                for (XulComponent xulcomponent : xulComponents) {
                    xulcomponent.setVisible(false);
                }

                if (LogicType.CUSTOMIZE.getId() == ldrClassificationEntity.getLogicType()) {
                    XulMenuitem viewDetail = (XulMenuitem)pluginMenu.getElementById("category-menu-delete");
                    viewDetail.setVisible(true);
                    viewDetail.setCommand(
                        "spoon.categoryMenuDelete('" + ldrCategoryEntity.getId() + "', '" + classificationEntityId + "')");
                }
                // 暂时没办法同级新增分组
                if (LogicType.CUSTOMIZE.getId() == ldrClassificationEntity.getLogicType()) {
                    XulMenuitem viewDetail = (XulMenuitem)pluginMenu.getElementById("category-menu-add");
                    viewDetail.setVisible(true);
                    viewDetail.setCommand("spoon.categoryNew('" + ldrCategoryEntity.getId() + "', '"
                        + classificationEntityId + "', '" + ldrCategoryEntity.getPluginTypeClass() + "')");
                }
                if (LogicType.CUSTOMIZE.getId() == ldrClassificationEntity.getLogicType()) {
                    XulMenuitem viewDetail = (XulMenuitem)pluginMenu.getElementById("category-menu-update");
                    viewDetail.setVisible(true);
                    viewDetail.setCommand("spoon.categoryUpdate('" + ldrCategoryEntity.getId() + "', '"
                        + classificationEntityId + "', '" + ldrCategoryEntity.getPluginTypeClass() + "')");
                }

                ConstUI.displayMenu(pluginMenu, LaniusClassTree.this);
            }

        }
    }

    class LaniusClassTreeKeyListener extends KeyAdapter {
        @Override
        public void keyPressed(KeyEvent e) {
            // CTRL-W or CTRL-F4 : close tab
            if ((e.keyCode == 'w' && (e.stateMask & SWT.CONTROL) != 0)
                || (e.keyCode == SWT.F4 && (e.stateMask & SWT.CONTROL) != 0)) {
                spoon.closeFile();
            }
            // CTRL-F5 : metastore explorer
            if (e.keyCode == SWT.F5 && (e.stateMask & SWT.CONTROL) != 0) {
                new MetaStoreExplorerDialog(spoon.getShell(), spoon.metaStore).open();
            }
        }
    }

}
